package com.faxsun.controller.uc.entity;

import java.util.Date;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.util.TransactionUtils;
import org.broadleafcommerce.common.web.BroadleafRequestContext;
import org.broadleafcommerce.common.web.BroadleafWebRequestProcessor;
import org.broadleafcommerce.profile.core.domain.Customer;
import org.broadleafcommerce.profile.core.service.CustomerService;
import org.broadleafcommerce.profile.web.core.CustomerState;
import org.broadleafcommerce.profile.web.core.service.login.LoginService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.WebRequest;

import com.faxsun.profile.core.domain.FSCustomer;
import com.faxsun.profile.core.domain.FSCustomerImpl;
import com.faxsun.profile.core.domain.FSThirdPartyCustomer;
import com.faxsun.profile.core.exception.AccountBindedThirdAccountException;
import com.faxsun.profile.core.exception.ThirdAccountBindToMultipleAccountException;
import com.faxsun.profile.core.exception.ThirdAccountBindedException;
import com.faxsun.profile.core.service.FSCustomerService;
import com.faxsun.profile.core.service.FSThirdPartyCustomerService;
import com.faxsun.web.notification.FaxsunInfoNotfier;
import com.faxsun.web.notification.exception.FaxsunInfoBindFailException;
import com.faxsun.web.notification.exception.ParameterEmptyException;
import com.faxsun.web.notification.exception.SessionTimeoutExceptiont;

@Service("fsThirdPartyService")
public class ThirdPartyLoginServer {

	private static final Log LOG = LogFactory.getLog(ThirdPartyLoginServer.class);

	@Resource(name = "blCartStateRequestProcessor")
	protected BroadleafWebRequestProcessor cartStateRequestProcessor;

	@Resource(name = "blCustomerStateRequestProcessor")
	protected BroadleafWebRequestProcessor customerStateRequestProcessor;

	@Resource(name = "blCustomerService")
	CustomerService customerService;

	@Resource(name = "blUserDetailsService")
	UserDetailsService userDetailService;

	@Resource(name = "blLoginService")
	LoginService blLoginService;

	@Resource(name = "thirdPartyContext")
	ThirdPartyContext context3rd;

	@Resource(name = "blThirdPartyCustomerService")
	FSThirdPartyCustomerService fscustomer3thService;

	@Resource(name = "fsThirdPartyService")
	ThirdPartyLoginServer loginServer3th;

	@Resource(name = "fsInfoNotifier")
	FaxsunInfoNotfier faxsunInfoNotifier;

	protected static final String HOME = "redirect:/";
	protected static final String COMPLETE_INFO = "";
	private static final String DEFAULT_PWD = "faxsun123";
	// private final String FAIL = "redirect:/";

	public String handle(HttpServletRequest request, HttpServletResponse response, String path, String channelId) {
		if (channelId == null || channelId.isEmpty())
			channelId = request.getParameter("channelId");
		return context3rd.loginContext(channelId, path, request, response);
	}

	public void requestURL(HttpServletRequest request, HttpServletResponse response) {
		String channel = request.getParameter("channelId");
		context3rd.responseContext(channel, request, response);
	}

	public void loginDirectly(Customer customer) {
		UserDetails principal = userDetailService.loadUserByUsername(customer.getUsername());
		Authentication authentication = new UsernamePasswordAuthenticationToken(principal, null,
				principal.getAuthorities());
		SecurityContextHolder.getContext().setAuthentication(authentication);
		customerStateRequestProcessor.process(getWebRequest());
		cartStateRequestProcessor.process(getWebRequest());
	}

	public String registerThenLogin(String username, String uid, String channelId, String avatar,String unionId) {
		FSCustomer customer = register3thCustomer(username, uid, channelId, avatar,null);
		blLoginService.loginCustomer(customer);
		return HOME;
	}

	@Transactional(TransactionUtils.DEFAULT_TRANSACTION_MANAGER)
	public FSCustomer register3thCustomer(String username, String uid, String channelId, String avatar,
			String unionId) {
		FSCustomerImpl customer = (FSCustomerImpl) customerService.createCustomer();
		customer.setId(customerService.findNextCustomerId());
		customer.setFirstName(username);
		String password = DEFAULT_PWD;
		customer.setUsername(uid);
		if (channelId.equals(CHANNEL.K360.toString())) {
			customer.setVerified(true);
		}
		customer.setMerged(false);
		customer.setIsCustomer3th(true);
		((FSCustomerService) customerService).easyRegisterCustomer(customer, password);

		FSThirdPartyCustomer fscustomer = fscustomer3thService.create3thCustomerByUid(uid);
		fscustomer.setChannelId(channelId);
		fscustomer.setCustomer(customer);
		fscustomer.setAvatar(avatar);
		fscustomer.setNickName(username);
		if (!StringUtils.isEmpty(unionId))
			fscustomer.setUnionId(unionId);
		fscustomer3thService.save(fscustomer);
		return customer;
	}

	private WebRequest getWebRequest() {
		return BroadleafRequestContext.getBroadleafRequestContext().getWebRequest();
	}

	@Transactional(TransactionUtils.DEFAULT_TRANSACTION_MANAGER)
	public FSCustomer getPhoneUUIDCustomer(String phoneNumber, String uuid) {
		FSCustomer customer = (FSCustomer) customerService.readCustomerByUsername(phoneNumber, true);
		if (customer == null) {
			// create account by phone number and device ID(uuid)
			customer = loginServer3th.register3thCustomer(phoneNumber, uuid, CHANNEL.DEVICE_ID.toString(), null,null);
			customer.setMobile(phoneNumber);
			customer.setVerified(true);
			customer.setUsername(phoneNumber);
			customer.setUnencodedPassword(phoneNumber);
			customerService.saveCustomer(customer);
			blLoginService.loginCustomer(customer);
		} else {
			if (!fscustomer3thService.checkCustomerBy3thUid(uuid)) {
				FSThirdPartyCustomer fscustomer3th = fscustomer3thService.create3thCustomerByUid(uuid);
				fscustomer3th.setCustomer(customer);
				fscustomer3thService.save(fscustomer3th);
			} else { // device ID may bind to another phone.
				FSThirdPartyCustomer fscustomer3th = fscustomer3thService.getCustomer3thByUid(uuid);
				fscustomer3th.setCustomer(customer);
				fscustomer3thService.save(fscustomer3th);
			}
			loginServer3th.loginDirectly(customer);
		}
		return customer;
	}

	public String getMD5byTVOrderInfo(Long customerId, Long productId) {
		Date d = new Date(System.currentTimeMillis());
		String str = customerId.toString() + productId.toString() + d.toString();
		return AssistTool.parseStrToMd5L32(str);
	}

	/**
	 * 传递给第三方上下文类，执行绑定请求的跳转验证
	 * 
	 * @param channelId
	 * @param request
	 * @param response
	 */
	public void requestBindURL(String channelId, HttpServletRequest request, HttpServletResponse response) {
		context3rd.requestBindContext(channelId, request, response);
	}

	/**
	 * 处理第三方帐户绑定的回调，将第三方帐户信息bind到当前帐户中
	 * 
	 * @param channelId
	 * @param request
	 * @param response
	 * @return
	 * @throws ThirdAccountBindToMultipleAccountException
	 * @throws ThirdAccountBindedException
	 * @throws AccountBindedThirdAccountException
	 * @throws FaxsunInfoBindFailException
	 * @throws SessionTimeoutExceptiont
	 */
	@Transactional(value = TransactionUtils.DEFAULT_TRANSACTION_MANAGER, rollbackFor = {
			ThirdAccountBindedException.class, ThirdAccountBindToMultipleAccountException.class,
			AccountBindedThirdAccountException.class, FaxsunInfoBindFailException.class, SessionTimeoutExceptiont.class,
			Exception.class })
	public boolean processBind(String channelId, HttpServletRequest request, HttpServletResponse response)
			throws ThirdAccountBindedException, ThirdAccountBindToMultipleAccountException,
			AccountBindedThirdAccountException, FaxsunInfoBindFailException, SessionTimeoutExceptiont {
		boolean result = false;
		ThirdPartyObj thirdObj = context3rd.parseThirdAccount(channelId, request, response);
		if (thirdObj == null) {
		} else {
			Customer customer = CustomerState.getCustomer();
			if (customer == null) {

			} else if (customer.isAnonymous()) {
				throw new SessionTimeoutExceptiont();
			} else {
				result = fscustomer3thService.bindAccount(channelId, thirdObj.getUid(), customer.getId(), "", true,
						thirdObj.getUniondId(), thirdObj.getAvatar(), thirdObj.getNickName());
				for (int i = 0; i < 3; i++) {
					// TODO: 将通知存入数据库，由另一个线程进行调用处理
					// 如果通知失败，进行重试
					boolean infoResult;
					try {
						infoResult = this.faxsunInfoNotifier.notifyBindAccount(customer.getId(), thirdObj.getUid(),
								channelId, thirdObj.getUniondId());
						if (infoResult) {
							result = true;
							break;
						}
					} catch (ParameterEmptyException e) {
						// e.printStackTrace();
						LOG.error("", e);
					}

					result = false;
				}
				if (!result) {
					throw new FaxsunInfoBindFailException(
							"customerId=" + customer.getId() + " unionId=" + thirdObj.getUniondId());
				}
			}
		}
		return result;
	}
}
